#ifndef SHADER_H
#define SHADER_H

#include "LibGL/OpenGL.h"
#include "Basic/GxLibBasic.h"
#include "LibGL/GxMath3D.h"
#include "Regex/MetaLexer.h"

namespace FinalRendering
{
	namespace Engine
	{
		using namespace GxLibBasic;
		using namespace GxMetaParser;

		class GPU_Shader : public Object
		{
		private:
			UInt handle;
		public:
			GPU_Shader(const String & fileName);
			~GPU_Shader();
			UInt GetHandle();
		};

		class Program : public Object
		{
		public:
			struct Technique
			{
				enum _Process
				{
					Normal, Reflect, ReflectAndRefract
				};
				String name;
				UInt handle;
				Array<RefPtr<GPU_Shader>> shaders;
				Array<String> autoUniforms;
				_Process process;
				enum _BlendFunc
				{
					None, AlphaBlend, Add
				} blendFunc;

				Technique();
				~Technique();
				void Link();
			};
		private:
			int defaultTechnique, reflectTechnique;
			Technique * curTech;
			Array<RefPtr<Technique>> techniques;
			String errString;
			String fileName;
			static RefPtr<MetaLexer> lexer;
			void ParseShaderNames(RefPtr<Technique> & tech, LexStream::LinkNode *& node);
			void ParseAutoUniforms(RefPtr<Technique> & tech, LexStream::LinkNode *& node);
			void ParseState(RefPtr<Technique> & tech, LexStream::LinkNode *& node);
			void ParseTechnique(LexStream::LinkNode *& node);
			void ParseProgramConfig(const String & config);
		public:
			
			Program(const String & fileName);
			~Program();
			static void Finalize();
			UInt GetHandle();
			Technique::_Process GetProcess();
			enum TechniqueType
			{
				NormalTechnique, ReflectTechnique
			};
			void SetTechnique(TechniqueType tech);
			void Bind();
			void UnBind();
			bool Validate();
			void BindFragDataLocation(const String & outputVar, int drawBufferID);
			int GetUniformLocation(const String & name);
			void SetUniform(const String & name, int val);
			void SetUniform(const String & name, float val);
			void SetUniform(const String & name, const vec3 & val);
			void SetUniform(const String & name, const vec4 & val);
			void SetUniform(int loc, int val);
			void SetUniform(int loc, float val);
			void SetUniform(int loc, const vec3 & val);
			void SetUniform(int loc, const vec4 & val);
			int GetAutoUniformCount();
			String GetAutoUniform(int i);
			bool IsReflective();
		};
	}
}

#endif